/*****************************************************************************
 *   Copyright(C)2009-2022 by VSF Team                                       *
 *                                                                           *
 *  Licensed under the Apache License, Version 2.0 (the "License");          *
 *  you may not use this file except in compliance with the License.         *
 *  You may obtain a copy of the License at                                  *
 *                                                                           *
 *     http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                           *
 *  Unless required by applicable law or agreed to in writing, software      *
 *  distributed under the License is distributed on an "AS IS" BASIS,        *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 *  See the License for the specific language governing permissions and      *
 *  limitations under the License.                                           *
 *                                                                           *
 ****************************************************************************/
/*============================ INCLUDES ======================================*/

#include "../driver.h"

#if VSF_HAL_USE_USART == ENABLED

#include "../vendor/plf/aic8800/src/driver/uart/reg_uart1.h"
#include "../vendor/plf/aic8800/src/driver/iomux/reg_iomux.h"
#include "../vendor/plf/aic8800/src/driver/ipc/reg_ipc_comreg.h"
#include "sysctrl_api.h"

/*============================ MACROS ========================================*/

#define VSF_USART_CFG_IMPLEMENT_OP                      ENABLED
#define VSF_USART_CFG_IMPLEMENT_REQUEST_BY_FIFO         ENABLED
#define VSF_USART_CFG_INSTANCE_PREFIX                   vsf_hw

#ifndef VSF_HW_USART_COUNT
#   error "Please define macro VSF_HW_USART_COUNT"
#else
#   define VSF_USART_CFG_TEMPLATE_COUNT                 VSF_HW_USART_COUNT
#endif

#ifdef  VSF_HW_USART_MASK
#   define VSF_USART_CFG_TEMPLATE_MASK                  VSF_HW_USART_MASK
#endif

/*============================ MACROFIED FUNCTIONS ===========================*/

#define VSF_USART_CFG_IMP_LV0(__count, __dont_care)                             \
    static const vsf_hw_usart_clock_t vsf_usart##__count##_clock = {            \
        .hclk   = CSC_HCLKME_UART##__count##_EN_BIT,                            \
        .oclk   = CSC_OCLKME_UART##__count##_EN_BIT,                            \
        .perclk = PER_UART##__count,                                            \
    };                                                                          \
    static vsf_hw_usart_t __vsf_usart##__count = {                              \
        VSF_USART_OP                                                            \
        .reg    = UART##__count,                                                \
        .irqn   = UART##__count##_IRQn,                                         \
        .clock = &vsf_usart##__count##_clock,                                   \
    };                                                                          \
    vsf_usart_request_t vsf_usart##__count = {                                  \
        VSF_USART_FIFO2REQ_OP                                                   \
        .real_usart_ptr = (vsf_usart_t *)&__vsf_usart##__count,                 \
    };                                                                          \
    void UART##__count##_IRQHandler(void)                                       \
    {                                                                           \
        __vsf_hw_usart_irq_handler(&__vsf_usart##__count);                      \
    }

/*============================ TYPES =========================================*/

typedef struct vsf_hw_usart_clock_t {
    uint32_t hclk;
    uint32_t oclk;
    uint32_t perclk;
} vsf_hw_usart_clock_t;

typedef struct vsf_hw_usart_t {
#if VSF_USART_CFG_MULTI_CLASS == ENABLED
    vsf_usart_t vsf_usart;
#endif

    vsf_usart_isr_t     isr;

    const vsf_hw_usart_clock_t *clock;

    IRQn_Type irqn;
    uart_reg_t *reg;

    usart_status_t       status;
    em_usart_irq_mask_t  irq_mask;
    uart_reg_t           reg_dump[10];
    uint8_t              index;
} vsf_hw_usart_t;

/*============================ IMPLEMENTATION ================================*/

vsf_err_t vsf_hw_usart_init(vsf_usart_t *usart_ptr, usart_cfg_t *cfg_ptr)
{
    vsf_hw_usart_t *hw_usart_ptr = (vsf_hw_usart_t *)usart_ptr;
    VSF_HAL_ASSERT(NULL != hw_usart_ptr);
    uart_reg_t *reg = hw_usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != reg);
    const vsf_hw_usart_clock_t *clock = hw_usart_ptr->clock;

    cpusysctrl_hclkme_set(clock->hclk);                                     // clock enable
    cpusysctrl_oclkme_set(clock->oclk);

    reg->DBUFCFG_REG = 0x0;                                                 // reset fifo
    reg->IRQCTL_REG = 0x0;                                                  // clean all interrupt
    reg->DBUFTH_REG = (1 << UART_RXTRIGTH) |  (0 << UART_TXTRIGTH);         // tx and rx data buffer trigger threshold
    reg->MDMCFG_REG |= UART_CLK_P_MSK;                                      // force 48M Clock
    reg->DFMTCFG_REG = cfg_ptr->mode & __USART_AIC8800_MASK;

    uint32_t div = sysctrl_clock_get(clock->perclk) / cfg_ptr->baudrate;
    reg->DFMTCFG_REG |= UART_DIVAE_MSK;                                     // div reg access enable
    reg->DIV0_REG = (div >> 4) & UART_DIV0_MSK;
    reg->DIV1_REG = (div >> 12) & UART_DIV1_MSK;
    reg->DIV2_REG = (div & 0x1) + ((div >> 1) & 0x7) + ((div << 3) & 0x7);
    reg->DFMTCFG_REG &= ~UART_DIVAE_MSK;                                    // div reg access disable

    reg->DBUFCFG_REG = UART_DBUFEN_MSK | UART_RXDRST_MSK | UART_TXDRST_MSK; // tx/rx data buf reset and enable

    vsf_usart_isr_t *isr_ptr = &cfg_ptr->isr;
    hw_usart_ptr->isr = *isr_ptr;

    if (isr_ptr->handler_fn != NULL) {
        NVIC_SetPriority(hw_usart_ptr->irqn, (uint32_t)isr_ptr->prio);
        NVIC_EnableIRQ(hw_usart_ptr->irqn);
    } else {
        NVIC_DisableIRQ(hw_usart_ptr->irqn);
    }

    return VSF_ERR_NONE;
}

fsm_rt_t vsf_hw_usart_enable(vsf_usart_t *usart_ptr)
{
    return fsm_rt_cpl;
}

fsm_rt_t vsf_hw_usart_disable(vsf_usart_t *usart_ptr)
{
    return fsm_rt_cpl;
}

void vsf_hw_usart_irq_enable(vsf_usart_t *usart_ptr, em_usart_irq_mask_t irq_mask)
{
    vsf_hw_usart_t *hw_usart_ptr = (vsf_hw_usart_t *)usart_ptr;
    VSF_HAL_ASSERT(NULL != hw_usart_ptr);
    VSF_HAL_ASSERT(NULL != hw_usart_ptr->reg);
    VSF_HAL_ASSERT((irq_mask & ~USART_IRQ_MASK_FIFO) == 0);

    hw_usart_ptr->reg->IRQCTL_REG |= irq_mask;
}

void vsf_hw_usart_irq_disable(vsf_usart_t *usart_ptr, em_usart_irq_mask_t irq_mask)
{
    vsf_hw_usart_t *hw_usart_ptr = (vsf_hw_usart_t *)usart_ptr;
    VSF_HAL_ASSERT(NULL != hw_usart_ptr);
    VSF_HAL_ASSERT(NULL != hw_usart_ptr->reg);
    VSF_HAL_ASSERT((irq_mask & ~USART_IRQ_MASK_FIFO) == 0);

    hw_usart_ptr->reg->IRQCTL_REG &= ~irq_mask;
}

usart_status_t vsf_hw_usart_status(vsf_usart_t *usart_ptr)
{
    vsf_hw_usart_t *hw_usart_ptr = (vsf_hw_usart_t *)usart_ptr;
    VSF_HAL_ASSERT(NULL != hw_usart_ptr);

    return hw_usart_ptr->status;
}

static bool __hw_usart_read_fifo_is_empty(vsf_hw_usart_t *hw_usart_ptr)
{
    VSF_HAL_ASSERT(NULL != hw_usart_ptr);
    uart_reg_t *reg = hw_usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != reg);

    return reg->DBUFSTS_REG & UART_RX_DBUF_EMPTY_MSK;
}

static bool __hw_usart_write_fifo_is_full(vsf_hw_usart_t *hw_usart_ptr)
{
    VSF_HAL_ASSERT(NULL != hw_usart_ptr);
    uart_reg_t *reg = hw_usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != reg);

    return reg->DBUFSTS_REG & UART_TX_DBUF_FULL_MSK;
}

uint_fast16_t vsf_hw_usart_fifo_read(vsf_usart_t *usart_ptr, void *buffer_ptr, uint_fast16_t count)
{
    vsf_hw_usart_t *hw_usart_ptr = (vsf_hw_usart_t *)usart_ptr;
    VSF_HAL_ASSERT(NULL != hw_usart_ptr);
    uart_reg_t *reg = hw_usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != reg);

    uint8_t *buf_ptr = (uint8_t *) buffer_ptr;
    VSF_HAL_ASSERT(NULL != buf_ptr);

    uint_fast16_t i = 0;
    while (i < count) {
        if (__hw_usart_read_fifo_is_empty(hw_usart_ptr)) {
            break;
        }
        buf_ptr[i++] = reg->TXRXD_REG & UART_TXDATA_MSK;
    }
    return i;
}

uint_fast16_t vsf_hw_usart_fifo_write(vsf_usart_t *usart_ptr, void *buffer_ptr, uint_fast16_t count)
{
    vsf_hw_usart_t *hw_usart_ptr = (vsf_hw_usart_t *)usart_ptr;
    VSF_HAL_ASSERT(NULL != hw_usart_ptr);
    uart_reg_t *reg = hw_usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != reg);

    uint8_t *buf_ptr = (uint8_t *) buffer_ptr;
    VSF_HAL_ASSERT(NULL != buf_ptr);

    uint_fast16_t i = 0;
    while (i < count) {
        if (__hw_usart_write_fifo_is_full(hw_usart_ptr)) {
            break;
        }
        reg->TXRXD_REG = buf_ptr[i++] & UART_TXDATA_MSK;
    }
    return i;
}

static em_usart_irq_mask_t __get_uart_irq_mask(vsf_hw_usart_t *hw_usart_ptr)
{
    VSF_HAL_ASSERT(NULL != hw_usart_ptr);
    uart_reg_t *reg = hw_usart_ptr->reg;
    VSF_HAL_ASSERT(NULL != reg);

    uint32_t value;
    uint32_t irq_type = reg->IRQTYP_REG & UART_IRQTYP_MSK;

    switch (irq_type) {
        case UART_IRQTYP_RX_ERROR_INT:
            value = reg->IRQSTS_REG;        // TODO: report more specific rx error
            (void)value;
            return USART_IRQ_MASK_RX_ERROR;

        case UART_IRQTYP_RX_INT:
            return USART_IRQ_MASK_RX;

        case UART_IRQTYP_TIMEOUT_INT:
            return 0;

        case UART_IRQTYP_TX_INT:
            return USART_IRQ_MASK_TX;

        case UART_IRQTYP_MODEM_INT:
            value = reg->MDMSTS_REG;        // TODO: report more specific moden line interrupt
            (void)value;
            return (em_usart_irq_mask_t)0;

        case UART_IRQTYP_NO_INT:
            return (em_usart_irq_mask_t)0;

        default:
            VSF_HAL_ASSERT(0);
            return (em_usart_irq_mask_t)0;
    }
}

static void __vsf_hw_usart_irq_handler(vsf_hw_usart_t *hw_usart_ptr)
{
    em_usart_irq_mask_t irq_mask = __get_uart_irq_mask(hw_usart_ptr);
    if (irq_mask & USART_IRQ_MASK) {
        if (NULL != hw_usart_ptr->isr.handler_fn) {
            hw_usart_ptr->isr.handler_fn(hw_usart_ptr->isr.target_ptr,
                                         (vsf_usart_t *)hw_usart_ptr, irq_mask);
        }
    }
}

/*============================ INCLUDES ======================================*/

#include "hal/driver/common/usart/usart_template.inc"

#endif      // VSF_HAL_USE_USART
